use sdl2::pixels::Color;
use sdl2::{Sdl, VideoSubsystem};
use sdl2::rect::Rect;
use sdl2::rect::Point;
use std::fmt;
use rand;
use rand::Rng;

pub const BLACK: Color = Color{r:0, g:0, b:0, a:100};
pub const WHITE: Color = Color{r:255, g:255, b:255, a:100};
pub const LIGHT_GRAY: Color = Color{r:76, g:76, b:76, a:100};
pub const DARK_GRAY: Color = Color{r:26, g:26, b:26, a:100};
pub const GREEN: Color = Color{r:0, g:255, b:0, a:100};
pub const CYAN: Color = Color{r:0, g:255, b:255, a:100};
pub const RED: Color = Color{r:255, g:0, b:0, a:100};
pub const BLUE: Color = Color{r:0, g:0, b:255, a:100};
pub const ORANGE: Color = Color{r:255, g:127, b:0, a:100};
pub const YELLOW: Color = Color{r:255, g:255, b:0, a:100};
pub const PURPLE: Color = Color{r:127, g:0, b:255, a:100};

pub fn get_shape_color(shape_index: i32) -> Color {
    match shape_index {
        0 => PURPLE,
        1 => YELLOW,
        2 => RED,
        3 => GREEN,
        4 => ORANGE,
        5 => BLUE,
        6 => CYAN,
        _ => BLACK
    }
}

pub fn get_level(score: i32) -> i32 {
    match score {
        0 ... 100 => 0,
        101 ... 300 => 1,
        301 ... 600 => 2,
        601 ... 1000 => 3,
        1001 ... 2000 => 4,
        2001 ... 4000 => 5,
        4001 ... 10000 => 6,
        10001 ... 100000000 => 7,
        _ => 0
    }
}

/// 7种形状
pub const SHAPE_COUNT: u8 = 7;
/// 4个方块
pub const POINT_COUNT: u8 = 4;

/// 11列
pub const COL_COUNT: u8 = 11;
/// 20行
pub const ROW_COUNT: u8 = 20;

pub const CELL_SIZE: i32 = 30;
pub const LEFT_MARGIN: i32 = 20;
pub const TOP_MARGIN: i32 = 30;

pub const BOARD_WIDTH: u32 = CELL_SIZE as u32 * COL_COUNT as u32;
pub const BOARD_HEIGHT: u32 = CELL_SIZE as u32 * ROW_COUNT as u32;

pub const BOARD_MARGIN_RIGHT: u32 = 20;
pub const PREVIEW_BOARD_COUNT: u8 = 4;
pub const PREVIEW_BOARD_WIDTH: u32 = PREVIEW_BOARD_COUNT as u32 * CELL_SIZE as u32;

pub const WIDTH: u32 = LEFT_MARGIN as u32 + BOARD_WIDTH + BOARD_MARGIN_RIGHT + PREVIEW_BOARD_WIDTH + BOARD_MARGIN_RIGHT;
pub const HEIGHT: u32 = BOARD_HEIGHT as u32 + TOP_MARGIN as u32 * 2;

const ACTION_FONT: &'static str = "assets/3dm.ttf";

#[derive(Copy, Clone, Debug)]
pub struct MyPoint {
    pub x: i16,
    pub y: i16,
}

impl fmt::Display for MyPoint {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum GridCellType {
    /// 空的方格
    Void,
    /// 已经掉下去，固定了的方块
    Fixed,
    /// 一个动的方块，还没用放下去
    Shape,
    /// 预览将要下去得位置
    Ghost,
}

/// The tetris game board consists of a two-dimensional array of GridCell's.
#[derive(Copy, Clone, Debug)]
pub struct GridCell {
    /// The type of cell enum, see GridCellType
    pub cell_type: GridCellType,
    /// 表明形状
    pub shape_index: i32,
}

/// Default GridCell's shape_index to -1 instead of 0
impl Default for GridCell {
    #[inline]
    fn default() -> GridCell {
        GridCell {
            cell_type: GridCellType::Void,
            shape_index: -1
        }
    }
}

pub struct Tetris {
    pub grid: [[GridCell; ROW_COUNT as usize]; COL_COUNT as usize],//[[cell;20];11]
    /// Game over flag
    pub game_over: bool,
    pub pause: bool,
    pub shape: [MyPoint; POINT_COUNT as usize],
    pub shape_index: i32,
    pub next_shape: [MyPoint; POINT_COUNT as usize],
    pub next_shape_index: i32,
    pub col: i32,
    pub row: i32,
    pub rng: rand::prelude::ThreadRng,
    pub score: i32,
    pub level: i32,
}

impl Tetris {
    pub fn new() -> Tetris {
        Tetris {
            grid: [[GridCell::default(); ROW_COUNT as usize]; COL_COUNT as usize],
            game_over: true,
            shape: SHAPES[0],
            next_shape: SHAPES[0],
            col: 0,
            row: 0,
            rng: rand::thread_rng(),
            shape_index: 0,
            next_shape_index: 0,
            score: 0,
            pause: false,
            level: 0,
        }
    }

    ///重置游戏
    pub fn reset_game(&mut self){
        self.grid = [[GridCell::default(); ROW_COUNT as usize]; COL_COUNT as usize];
        self.shape = SHAPES[0];
        self.next_shape = SHAPES[0];
        self.col = 0;
        self.row = 0;
        self.rng = rand::thread_rng();
        self.score = 0;
        self.pause = false;
    }

    ///开始游戏
    pub fn start_game(&mut self){
        if self.game_over {
            self.reset_game();
            self.game_over = false;
            self.next_shape_index = self.rng.gen_range(0, SHAPE_COUNT) as i32;
            self.next_shape = SHAPES[self.next_shape_index as usize];
            self.new_shape();
        }
    }

    ///新的形状
    pub fn new_shape(&mut self){
        self.row = 0;
        self.col = COL_COUNT as i32 / 2;
        self.shape = self.next_shape;
        self.shape_index = self.next_shape_index;
        self.next_shape_index = self.rng.gen_range(0, SHAPE_COUNT) as i32;
        self.next_shape = SHAPES[self.next_shape_index as usize];
        let use_row = self.row;
        let use_col = self.col;
        self.move_shape(use_col, use_row, false);
    }

    ///移动形状
    fn move_shape(&mut self, col: i32, row: i32, clear_befor: bool) {
        if clear_befor {
            //清理上一次的方块
            self.clear_shape();
        }
        self.col = col;
        self.row = row;
        let use_shape = self.shape;
        let use_col = self.col;
        let use_row = self.row;
        for point in use_shape.iter() {
            if self.point_in_bounds(use_col, use_row, *point) {
                self.grid[(self.col + point.x as i32) as usize][(self.row as i32 + point.y as i32) as usize].cell_type = GridCellType::Shape;
                self.grid[(self.col + point.x as i32) as usize][(self.row as i32 + point.y as i32) as usize].shape_index = self.shape_index;
            }
        }
    }

    fn sharp_to_downest(&self, col: i32, row: i32, shape: [MyPoint; POINT_COUNT as usize]) -> bool {
        let mut sharp_to_downest = false;
        for point in shape.iter() {
            let grid_point_x = col + point.x as i32;
            let grid_point_y = row + point.y as i32;
            if grid_point_y == ROW_COUNT as i32{//判断是否出下界
                sharp_to_downest = true;
                break;
            }
        }
        sharp_to_downest
    }

    ///判断是否出界
    fn point_in_bounds(&self, col: i32, row: i32, point: MyPoint) -> bool {
        let grid_point_x = col + point.x as i32;
        let grid_point_y = row + point.y as i32;

        grid_point_x >= 0 &&grid_point_x < COL_COUNT as i32 &&grid_point_y >= 0 &&grid_point_y < ROW_COUNT as i32
    }

    ///清理形状
    fn clear_shape(&mut self) {
        let use_shape = self.shape;
        let col = self.col;
        let row = self.row;
        for point in use_shape.iter() {
            if self.point_in_bounds(col, row, *point) {
                self.grid[(self.col + point.x as i32) as usize][(self.row as i32 + point.y as i32) as usize].cell_type = GridCellType::Void;
            }
        }
    }

    ///每次下降
    pub fn tick_down(&mut self){
        if !self.game_over&& !self.pause {
            let use_row = self.row + 1;
            let use_col = self.col;
            let use_shape = self.shape;
            if self.sharp_to_downest(use_col, use_row, use_shape)|| self.sharp_downto_fix(use_col, use_row, use_shape) {
                for point in use_shape.iter() {
                    let grid_point_x = (self.col + point.x as i32);
                    let grid_point_y = (self.row + point.y as i32);
                    if grid_point_x > -1&& grid_point_y > -1 {
                        self.grid[grid_point_x as usize][grid_point_y as usize].cell_type = GridCellType::Fixed;
                    }
                }
                //查找完成的行
                self.complete_down();
                //加等级
                self.level = get_level(self.score);
                if self.game_over() {
                    self.game_over = true;
                }else{
                    self.new_shape();
                }
            }else{
                self.move_shape(use_col, use_row, true);
            }

        }
    }

    ///一下到底
    pub fn drop_downtest(&mut self){
        if !self.game_over&& !self.pause {
            let mut row = self.row + 1;
            while self.set_row(row){
                row += 1;
            }
            self.tick_down();
        }
    }

    ///完成后消行
    fn complete_down(&mut self) {
        let mut current_row: i32 = ROW_COUNT as i32 - 1;
        while current_row >= 0 {
            let found_void = (0..COL_COUNT as usize).any(|c| self.grid[c][current_row as usize].cell_type == GridCellType::Void);
            if !found_void {
                //println!("{}", current_row);
                for col in 0..COL_COUNT as usize {
                    for temp_row in (0..current_row as usize).rev() {
                        self.grid[col][temp_row + 1].cell_type = self.grid[col][temp_row].cell_type;
                    }
                    self.grid[col][0].cell_type = GridCellType::Void;
                }
                //消除一行，分数+10
                self.score += 10;
            }else{
                current_row -= 1;
            }
        }

    }

    fn game_over(&self) -> bool {
        let y = 0usize;
        let mut game_over = false;
        for x in 0..COL_COUNT {
            if self.grid[x as usize][y].cell_type == GridCellType::Fixed {
                game_over = true;
                break;
            }
        }
        game_over
    }

    //是否下落到fix方块上
    fn sharp_downto_fix(&self, col: i32, row: i32, shape: [MyPoint; POINT_COUNT as usize]) -> bool {
        let mut sharp_downto_fix = false;
        for point in shape.iter() {
            let grid_point_x = col + point.x as i32;//0
            let grid_point_y = row + point.y as i32;
            if grid_point_x > -1 && grid_point_y > -1{
                if self.grid[grid_point_x as usize][grid_point_y as usize].cell_type == GridCellType::Fixed {
                    sharp_downto_fix = true;
                    break;
                }
            }

        }
        sharp_downto_fix
    }

    //形状碰撞固定格
    fn sharp_collision_fix(&self, col: i32, row: i32, shape: [MyPoint; POINT_COUNT as usize]) -> bool {
        let mut sharp_collision_fix = false;
        for point in shape.iter() {
            let grid_point_x = (col + point.x as i32);
            let grid_point_y = (row + point.y as i32);
            if grid_point_y > 0 {
                if self.grid[grid_point_x as usize][grid_point_y as usize].cell_type == GridCellType::Fixed {
                    sharp_collision_fix = true;
                    break;
                }
            }
        }
        sharp_collision_fix
    }

    ///得到占用矩形宽高，和最小x坐标，和最小y坐标， 用来寻找preview中心位置
    pub fn get_rect_min(&self, shape: [MyPoint; POINT_COUNT as usize]) -> (i32, i32, i32, i32){
        let mut width_vec: Vec<i32> = Vec::new();
        let mut height_vec: Vec<i32> = Vec::new();
        let mut min_x = 100i32;
        let mut min_y = 100i32;
        for point in shape.iter(){
            let mut contain_x = false;
            for x in width_vec.iter(){
                if *x  == point.x as i32 {
                    contain_x = true;
                }
                if *x < min_x {
                    min_x = *x;
                }
            };
            if !contain_x {
                width_vec.push(point.x as i32);
                //最后一次push没用添加判断大小
                if (point.x as i32) < min_x {
                    min_x = point.x as i32;
                }
            }
            let mut contain_y = false;
            for y in height_vec.iter(){
                if *y  == point.y as i32 {
                    contain_y = true;
                }
                if *y < min_y {
                    min_y = *y;
                }
            }
            if !contain_y {
                height_vec.push(point.y as i32);
                //最后一次push没用添加判断大小
                if (point.y as i32) < min_y {
                    min_y = point.y as i32;
                }
            }
        };
        (width_vec.len() as i32, height_vec.len() as i32, min_x, min_y)
    }

    pub fn set_col(&mut self, col: i32) {
        if !self.game_over&& !self.pause {
            let row = self.row;
            let use_shape = self.shape;
            //检查左右，是否可以移动 移动是否会重合
            if self.sharp_to_leftright(col, row, use_shape)&&!self.sharp_collision_fix(col, row, use_shape) {
                self.move_shape(col, row, true);
            }
        }
    }

    pub fn set_row(&mut self, row: i32) -> bool {
        let mut result = false;
        if !self.game_over&& !self.pause {
            let use_shape = self.shape;
            let use_col = self.col;
            let use_row = row;
            if !self.sharp_to_downest(use_col, use_row, use_shape)&& !self.sharp_downto_fix(use_col, use_row, use_shape) {
                self.move_shape(use_col, use_row, true);
                result = true;
            }
        }
        result
    }

    pub fn set_pause(&mut self){
        if !self.game_over {
            if self.pause {
                self.pause = false;
            }else{
                self.pause = true;
            }
        }
    }

    //旋转形状
    pub fn rotate_shape(&self, shape: &mut [MyPoint; POINT_COUNT as usize]){
        for point in &mut shape.iter_mut() {
            let old_y = point.y;
            point.y = point.x;
            point.x = -1 * old_y;
        }
    }

    //旋转
    pub fn rotate(&mut self){
        if !self.game_over&& !self.pause {
            let mut shape = self.shape;
            self.clear_shape();
            let use_col = self.col;
            let use_row = self.row;
            if self.shape_index != 1 {//不为方块时旋转
                self.rotate_shape(&mut shape);
            }
            //检测选择后的方块是否出界
            if self.sharp_to_leftright(use_col, use_row, shape)&&!self.sharp_to_downest(use_col, use_row, shape)&&!self.sharp_collision_fix(use_col, use_row, shape){
                self.shape = shape;
            }
            self.move_shape(use_col, use_row, true);
        }
    }

    //是否可以向左右移动
    pub fn sharp_to_leftright(&self, col: i32, row: i32, shape: [MyPoint; POINT_COUNT as usize]) -> bool{
        let mut sharp_to_leftright = true;
        for point in shape.iter() {
            if (col + point.x as i32) < 0|| col + point.x as i32 >= COL_COUNT as i32 {
                sharp_to_leftright = false;
                break;
            }
        }
        sharp_to_leftright
    }

}

pub const SHAPES: [[MyPoint; POINT_COUNT as usize]; SHAPE_COUNT as usize] = [
    /*
                  0,-1
             -1,0 0, 0 1,0
    */
    [MyPoint { x: -1, y: 0 }, MyPoint { x: 0, y: -1 }, MyPoint { x: 0, y: 0 }, MyPoint { x: 1, y: 0 }],
    // see also SQUARE_SHAPE_INDEX const below
    /*
        -1,-1 0,-1
        -1, 0 0, 0
    */
    [MyPoint { x: 0, y: 0 }, MyPoint { x: -1, y: 0 }, MyPoint { x: -1, y: -1 }, MyPoint { x: 0, y: -1 }],
    /*
           -1,-1 0,-1
                 0, 0 1,0
    */
    [MyPoint { x: 0, y: 0 }, MyPoint { x: 0, y: -1 }, MyPoint { x: -1, y: -1 }, MyPoint { x: 1, y: 0 }],
    /*
              0,-1 1,-1
         -1,0 0, 0
    */
    [MyPoint { x: 0, y: 0 }, MyPoint { x: 0, y: -1 }, MyPoint { x: 1, y: -1 }, MyPoint { x: -1, y: 0 }],
    /*
                  1,-1
         -1,0 0,0 1, 0
    */
    [MyPoint { x: 0, y: 0 }, MyPoint { x: 1, y: 0 }, MyPoint { x: 1, y: -1 }, MyPoint { x: -1, y: 0 }],
    /*
         -1,-1
         -1, 0 0,0 1,0
    */
    [MyPoint { x: 0, y: 0 }, MyPoint { x: -1, y: 0 }, MyPoint { x: -1, y: -1 }, MyPoint { x: 1, y: 0 }],
    /*
        -2,0, -1,0, 0,0, 1,0
    */
    [MyPoint { x: -2, y: 0 }, MyPoint { x: -1, y: 0 }, MyPoint { x: 0, y: 0 }, MyPoint { x: 1, y: 0 }]
];